#ifndef M_RING_H
#define M_RING_H

#include <stdint.h>
#include <pthread.h>
#include <cstring>

class RingBuffer
{
    public:
        enum
        {
            RING_OVERLOAD_E         = -2,           //缓冲空间不足
            RING_EMPTY_E,                           //空的缓冲
            RING_OK_E,                              //操作成功
        }RING_OP_E;

        RingBuffer(std::string ring_name, uint32_t ring_size = 8192)
        :   r_idx(0)
        ,   w_idx(0)
        ,   name(ring_name)
        ,   max_size(ring_size)
        {
            pb = new uint8_t[max_size]();

            pthread_mutex_init(&lock, NULL);
        }

        ~RingBuffer()
        {
            delete [] pb;
            pthread_mutex_destroy(&lock);
        }

        void reset()
        {
            pthread_mutex_lock(&lock);
            r_idx = w_idx = 0;
            pthread_mutex_unlock(&lock);
        }

        int push(uint8_t *data, uint16_t data_size)
        {
            if (data == NULL && data_size == 0)
                return RING_EMPTY_E;
            int ret = RING_OK_E;
            uint32_t s1 = 0;
            uint32_t s2 = 0;
            pthread_mutex_lock(&lock);
            if (r_idx > w_idx)
            {
                s1 = r_idx - w_idx;
                s2 = 0;
            }
            else if (r_idx <= w_idx)
            {
                s1 = max_size - w_idx;
                s2 = r_idx;
            }
            else
            {
                s1 = 0;
                s2 = 0;
            }
            if ((s1 + s2) > (data_size + 2))
            {
                //先保存帧长度
                push_length(data_size);
                if (s1 >= 2)
                {
                    s1 -= 2;
                }
                else if (s1 >= 1)
                {
                    s1 -= 1;
                    s2 -= 1;
                }
                else
                {
                    s2 -= 2;
                }
                //保存数据
                if (s1 >= data_size)
                {
                    std::memcpy(&pb[w_idx], data, data_size);
                    w_idx += data_size;
                }
                else if (s1 > 0)
                {
                    std::memcpy(&pb[w_idx], data, s1);

                    w_idx = 0;
                    data_size -= s1;
                    std::memcpy(&pb[w_idx], data + s1, data_size);
                    w_idx += data_size;

                }
                else
                {
                    std::memcpy(&pb[w_idx], data, data_size);
                    w_idx += data_size;
                }
                w_idx = (w_idx >= max_size) ? (0) : (w_idx);
            }
            else
            {
                ret = RING_OVERLOAD_E;
            }
            pthread_mutex_unlock(&lock);
            return ret;
        }

        int hasData()
        {
            int ret = RING_OK_E;
            pthread_mutex_lock(&lock);
            if (r_idx == w_idx)
            {
                ret = RING_EMPTY_E;
            }
            pthread_mutex_unlock(&lock);
            return ret;
        }

        int pop(uint8_t* buf, uint16_t& buf_size)
        {
            if (buf == NULL && buf_size == 0)
                return RING_EMPTY_E;
            int ret = RING_OK_E;
            uint16_t length = 0;
            uint32_t s1 = 0;
            uint32_t s2 = 0;
            pthread_mutex_lock(&lock);
            if (r_idx != w_idx)
            {
                length = try_length();
                if (length <= buf_size)
                {
                    length = pop_length();
                    buf_size = length;
                    if (r_idx > w_idx)
                    {
                        s1 = max_size - r_idx;
                        s2 = w_idx;
                    }
                    else if (r_idx < w_idx)
                    {
                        s1 = w_idx - r_idx;
                        s2 = 0;
                    }
                    else
                    {
                        s1 = 0;
                        s2 = 0;
                        ret = RING_EMPTY_E;
                    }
                    if (s1 >= length)
                    {
                        std::memcpy(buf, &pb[r_idx], length);
                        r_idx += length;
                    }
                    else if (s1 > 0)
                    {
                        std::memcpy(buf, &pb[r_idx], s1);
                        r_idx = 0;
                        length -= s1;
                        std::memcpy(buf + s1, &pb[r_idx], length);
                        r_idx += length;
                    }
                    else
                    {
                        std::memcpy(buf, &pb[r_idx], length);
                        r_idx += length;
                    }
                    r_idx = (r_idx >= max_size) ? (0) : (r_idx);
                }
                else
                {
                    ret = RING_OVERLOAD_E;
                }
            }
            else
            {
                ret = RING_EMPTY_E;
            }
            pthread_mutex_unlock(&lock);
            return ret;
        }
    private:
        void push_length(uint16_t legnth)
        {
            pb[w_idx++] = legnth >> 8;
            w_idx = (w_idx >= max_size) ? (0) : (w_idx);
            pb[w_idx++] = legnth >> 0;
            w_idx = (w_idx >= max_size) ? (0) : (w_idx);
        }
        uint16_t pop_length(void)
        {
            uint16_t legnth;
            legnth = pb[r_idx++];
            r_idx = (r_idx >= max_size) ? (0) : (r_idx);
            legnth <<= 8;
            legnth |= pb[r_idx++];
            r_idx = (r_idx >= max_size) ? (0) : (r_idx);
            return legnth;
        }
        uint16_t try_length(void)
        {
            uint16_t legnth;
            uint32_t idx = r_idx;
            legnth = pb[idx++];
            idx = (idx >= max_size) ? (0) : (idx);
            legnth <<= 8;
            legnth |= pb[idx++];
            return legnth;
        }
    private:
        pthread_mutex_t             lock;
        std::string                 name;
        uint32_t                    r_idx;
        uint32_t                    w_idx;
        uint32_t                    max_size;
        uint8_t*                    pb;
};


#endif